import { BaseCommand, IStatusDo } from '../types/common.interface';
import { Subject, Observable } from 'rxjs';
import { publish, refCount } from 'rxjs/operators';
import { Hook } from './hook';
import { Injectable } from '../decorators/inject';
import { RenderTrigger } from './render-trigger';
import { Config } from './config';

type CommandQueue = {
    command: BaseCommand;
    pre: null|CommandQueue;
    next: null|CommandQueue;
}
@Injectable('ExecuteRecord')
export class ExecuteRecord {
    static pluginName = 'ExecuteRecord';
    protected store: CommandQueue|null = {
        pre: null,
        next: null,
        command: null
    };
    protected statusChange$: Subject<{canNext: boolean, canRedo: boolean}>;
    constructor(
        protected hook: Hook,
        protected renderTrigger: RenderTrigger,
        protected conf: Config
    ) {
        let temp = Observable.create((observer: Subject<any>) => {this.statusChange$ = observer;}).pipe(publish());
        temp.connect();
    }
    public init() {

    }
    public destroy() {

    }
    protected limitMaxCommandQuene() {
        let maxRunCount = 1000;
        let len = 0;
        let current = this.store;
        while(true) {
            if(--maxRunCount === 0) {
                break;
            }
            if(current.command === null) {
                break;
            }
            if(len > this.conf.excuteMaxCommandStore) {
                current.pre = {pre: null, command: null, next: current};
                break;
            }
            len++;
            current = current.pre;
        }
    }
    public execute(command: BaseCommand) {
        let BeforeCommand = this.hook.beforeCommandExcute.excute({command: command});
        try {
            if(BeforeCommand.length > 0 && BeforeCommand[0] != null) {
                BeforeCommand[0].command.execute();
                let commandNode: CommandQueue = {
                    command: BeforeCommand[0].command,
                    pre: this.store,
                    next: null
                }
                this.store.next = commandNode;
                this.store = commandNode;
                this.triggerStatusChange();
                this.renderTrigger.triggerRenderAll();
            }     
        } catch (error) {
            // @ts-ignore
            console.warn('hook beforeCommandExcute is run error!' + new Error(error), BeforeCommand);
        }
        this.hook.afterCommandExcute.excute();
        this.limitMaxCommandQuene();
    }
    public nextDo() {
        try {
            const commandNode = this.store.next as CommandQueue;
            if(commandNode === null) {
                console.warn('can not forward do!')
                return;
            }
            let command = commandNode.command;
            this.store = commandNode;
            let BeforeCommand = this.hook.beforeCommandExcute.excute({command: command});
            BeforeCommand[0].command.execute();
            this.renderTrigger.triggerRenderAll();
            this.hook.afterCommandExcute.excute();
        } catch (error) {
            
        }
        this.triggerStatusChange();
        // this.renderTrigger.bgBodyRender$.next();
    }
    public redo() {
        const commandNode = this.store as CommandQueue;
        if(commandNode.command === null) {
            console.warn('can not redo!')
            return;
        }
        this.store = commandNode.pre;
        let BeforeCommand = this.hook.beforeCommandExcute.excute({command: commandNode.command});
        try {
            BeforeCommand[0].command.redo();
        } catch (error) {
            console.error(error)
        }
        this.triggerStatusChange();
        this.renderTrigger.triggerRenderAll();
        this.hook.afterCommandExcute.excute();
    }
    public canRedo() {
        if(this.store && this.store.command !== null) {
            return true;
        }
        return false;
    }
    public canNextDo() {
        if(this.store && this.store.next !== null) {
            return true;
        }
        return false;
    }
    protected triggerStatusChange() {
        let temp = {canNext: this.canNextDo(), canRedo: this.canRedo()};
        this.statusChange$.next(temp);
        this.hook.statusChange.excute(temp);
    }
}